﻿using FFmpeg.AutoGen;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using VideoPlayer.FFmpegHandle;

namespace VideoPlayer
{
    public partial class Test2 : Form
    {
        public Test2()
        {
            InitializeComponent();
        }
        Thread thPlayer;
        FFmpegDll fFmpegDll = null;
        private void Button1_Click(object sender, EventArgs e)
        {

            button1.Enabled = false;
            if (thPlayer != null)
            {
                thPlayer = null;
            }
            else
            {
                thPlayer = new Thread(DeCoding);
                thPlayer.IsBackground = true;
                thPlayer.Start(textBox1.Text.Trim());
                button1.Text = "停止播放";
                button1.Enabled = true;
            }
        }

        /// <summary>
        /// 播放线程执行方法
        /// </summary>
        private unsafe void DeCoding(object str)
        {
            try
            {
                Console.WriteLine("DeCoding run...");
                play(str.ToString());

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                Console.WriteLine("DeCoding exit");
                this.Invoke(new MethodInvoker(() =>
                {
                    button1.Text = "开始播放";
                    button1.Enabled = true;
                }));
            }
        }

        private void Test2_Load(object sender, EventArgs e)
        {
            FFmpegBinariesHelper.RegisterFFmpegBinaries();
            fFmpegDll = new FFmpegDll();
            Console.WriteLine($"FFmpeg version info: {ffmpeg.av_version_info()}");
            waveOut = new WaveOut();
            bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat());
            waveOut.Init(bufferedWaveProvider);
            waveOut.Play();
        }

        //播放状态
        private bool isPlaying = false;

        //缓存时间（ms）
        const int BUFFER_DURATION = 20000;

        //NAudio音频播放组件
        private WaveOut waveOut;
        private BufferedWaveProvider bufferedWaveProvider;

        private unsafe void DecodeAllFramesToImages(string url)
        {

        }

        /// <summary>
        /// 播放
        /// </summary>
        unsafe void play(string url)
        {
            isPlaying = true;

            bool isInitOk = false;
            while (isPlaying && !isInitOk)
            {
                //初始化ffmpeg
                int initFfmpeg = fFmpegDll.init_ffmpeg(url);
                if (initFfmpeg >= 0)
                {
                    isInitOk = true;
                    break;
                }
            }

            TimeSpan audioMaxBufferedDuration = new TimeSpan(0, 0, 0, 0, BUFFER_DURATION); //音频最大缓冲时间

            while (isPlaying)
            {
                int i = fFmpegDll.read_frame(); //读取一帧 1:音频 2:视频
                if (i == 1)
                {
                    if (bufferedWaveProvider.BufferedDuration.CompareTo(audioMaxBufferedDuration) > 0)
                    {
                        bufferedWaveProvider.ClearBuffer();
                    }

                    IntPtr audioFrame = (IntPtr)fFmpegDll.get_audio_frame(); //一帧音频句柄
                    int bufSize = fFmpegDll.get_audio_buffer_size(); //一帧音频占用大小
                    byte[] bytes = new byte[bufSize];
                    Marshal.Copy(audioFrame, bytes, 0, bufSize); //拷贝句柄中的音频到bytes
                    bufferedWaveProvider.AddSamples(bytes, 0, bufSize);//向缓存中添加音频样本
                }
                else if (i == 2)
                {
                    //try
                    //{
                    //    //var bitmap = new Bitmap(fFmpegDll.get_video_width(), fFmpegDll.get_video_height(),
                    //    //    fFmpegDll.get_linesize()[0], System.Drawing.Imaging.PixelFormat.Format24bppRgb, fFmpegDll.get_video_frame());
                    //    var convertedFrame = fFmpegDll.get_video_frame();
                    //    var bitmap = new Bitmap(convertedFrame.width, convertedFrame.height, convertedFrame.linesize[0], System.Drawing.Imaging.PixelFormat.Format24bppRgb, (IntPtr)convertedFrame.data[0]);
                    //    if (pictureBox1.InvokeRequired)
                    //    {
                    //        pictureBox1.Invoke(new Action<Bitmap>(p =>
                    //        {
                    //            pictureBox1.Image = p;
                    //        }), bitmap);
                    //    }
                    //    else
                    //    {
                    //        pictureBox1.Image = bitmap;
                    //    }
                    //}
                    //catch (Exception ex)
                    //{ }
                }
            }
        }

        /// <summary>
        /// 播放视频，单独开线程，是为了和音频有同步的缓存时间
        /// </summary>
        //void playVideo(object bs)
        //{
        //    int time = BUFFER_DURATION - 300;
        //    if (time > 0)
        //    {
        //        Thread.Sleep(time);
        //    }
        //    Dispatcher.BeginInvoke(new Action(delegate { pictureBox1.Image = bs as BitmapSource; }));
        //}

        /// <summary>
        /// 将字符串分配到非托管区，并返回该非托管区句柄
        /// </summary>
        /// <param name="strData">字符串</param>
        /// <returns>非托管区句柄</returns>
        private IntPtr mallocIntPtr(String strData)
        {
            //字符串转化为字节
            Byte[] bytesData = System.Text.Encoding.Default.GetBytes(strData);

            //分配非托管区内存
            IntPtr ptr = Marshal.AllocHGlobal(bytesData.Length);

            //清空非托管区
            Byte[] emptyBytes = new Byte[bytesData.Length + 1]; //一定要加1,否则后面是乱码，原因未找到
            Marshal.Copy(emptyBytes, 0, ptr, emptyBytes.Length);

            //往非托管区塞数据
            Marshal.Copy(bytesData, 0, ptr, bytesData.Length);

            return ptr;
        }
    }
}
